<template>
  <div>
    <h3>toRef响应式工具</h3>
    <p>
      将对象中的某一个比较常用的属性，或者是组件模板中的使用次数较多，为了使用方便,使用toRef对这个属性进行单独获取。
    </p>
    <h3>toRefs响应式工具</h3>
    <p>将整个响应对象的所有属性,使用toRef对每个属性进行单独获取。</p>

    <!-- 父传子：子组件接收 -->
    <p>a:{{ a }},b:{{ b }}</p>
    <button @click="sendData">发射事件</button>

    <!-- ref toRef -->
    <div style="background: lightblue">
      <!-- 想要获取到某个属性。通过打点，对象.属性名 获取-->
      <p>uname:{{ users.uname }}</p>
      <p>uage:{{ users.uage }}</p>
      <p>m:{{ users.other.data.m }}</p>
    </div>
    <h3>m:{{ users.other.data.m }}</h3>
    <!-- 使用toRef 写法更简便了 方便多个地方使用这个属性-->
    <h4>{{ m }}</h4>
    <h4>{{ m }}</h4>
    <h4>{{ m }}</h4>
    <button @click="setM">第一类修改m</button>
    <button @click="setMM">第二类修改m</button>

    <!-- reactive -->
    <div style="background: pink">
      <p>{{ panda.color }}</p>
      <p>{{ panda.other.weight }}</p>
      <button @click="changePanda1">正常修改panda</button>
    </div>
    <div style="background: orange">
      <p>{{ color }}</p>
      <p>{{ other.weight }}</p>
      <button @click="changePanda2">使用...toRefs修改panda</button>
    </div>
  </div>
</template>

<script>
import { reactive, toRef, ref, toRefs } from "vue";
export default {
  name: "ToRefToRefs",
  props: {
    a: Number,
    b: Number,
  },
  /* 如何使用父组件传过来的值？setup里面有两个内置的参数props和context可以使用
  如果不涉及父子传值，可以不写这两个参数*/
  /*
    两个参数(可以任意命名，只是习惯用props和context)：
    props : 接收父组件的传值
    context : 上下文对象，有三个属性：分别为：attr slots emit
    attrs : 等价于 this.$attrs 接收父组件有数据 但子组件未声明的props
    emit : 等价于 this.$emit 发射自定义事件，向父组件传值
  */
  setup(props, context) {
    // console.log(this);  // this指向undefined 不能使用
    console.log("第一个参数", props);
    console.log("第二个参数", context.attrs); // 接收了父组件传过来子组件未接收的m和n值
    console.log("slots", context.slots);

    const sendData = () => {
      context.emit("setData", { animals: "dog" }); // 一定要写父组件自定义的事件setData
    };

    /* 使用 toRef() 举例*/
    const users = reactive({
      uname: "小王",
      uage: 18,
      uaddress: "郑州市",
      other: {
        data: {
          m: 1000,
        },
      },
    });

    const setM = () => {
      users.other.data.m = 6666;
    };

    // const m = users.other.data.m; // 这种写法是会丢失响应性的，不建议这样写，除非真的不需要响应式数据了。

    // const m = toRef(users.other.data, "m"); // 将 users.other.data 里面的m属性，单独结构出来，并保证数据的响应性，所以使用toRef结构。它得到的RefImpl对象，具备响应性
    // console.log("m = ", m);
    // const setMM = () => {
    //   m.value = 3000;      // 这样声明，dom渲染时直接拿m即可
    // };

    const m = ref(users.other.data, "m"); // users.other.data.m 将这个数据，全新声明了一个ref对象，和源数据已经脱离关系了。
    const setMM = () => {
      m.value = 3000;
    };

    // 使用reactive
    const panda = reactive({
      color: "white",
      other: {
        weight: "圆滚滚胖嘟嘟",
        tall: 160,
      },
    });
    console.log(panda); // 返回的是一个proxy,响应性对象
    let result = toRefs(panda); //这个result不是响应性对象，里面的每一个属性(color,weight,tall)是响应式的。使用的时候一般不这样写，直接在return里面用展开运算符写...toRefs(panda)
    console.log(result);

    const changePanda1 = () => {
      panda.color = "black";
    };
    const changePanda2 = () => {
      panda.other.weight = "大熊猫";
    };

    return {
      sendData,
      users,
      setM,
      m,
      setMM,

      panda,
      changePanda1,
      changePanda2,
      ...toRefs(panda), // 使用这个渲染时写法是直接写属性，操作简单，不用写panda了
      // color   //ObjectRefImpl
      // other, // ObjectRefImpl
    };
  },
};
</script>

<style lang="less" scoped></style>
users
